package com.ybkj.o2o.zhuanche.service.impl;

import com.ybkj.o2o.zhuanche.common.chart.HourChart;
import com.ybkj.o2o.zhuanche.entity.ChartDay;
import com.ybkj.o2o.zhuanche.entity.ChartMonth;
import com.ybkj.o2o.zhuanche.entity.ChartYear;
import com.ybkj.o2o.zhuanche.entity.Order;
import com.ybkj.o2o.zhuanche.entity.OrderFinishedInfo;
import com.ybkj.o2o.zhuanche.mapper.ChartDayMapper;
import com.ybkj.o2o.zhuanche.mapper.ChartMonthMapper;
import com.ybkj.o2o.zhuanche.mapper.ChartYearMapper;
import com.ybkj.o2o.zhuanche.mapper.OrderFinishedInfoMapper;
import com.ybkj.o2o.zhuanche.service.ChartService;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by Administrator on 2016/12/8.
 */

@Service
public class DefaultChartService implements ChartService {
  @Autowired
  private ChartDayMapper chartDayMapper;
  @Autowired
  private ChartMonthMapper chartMonthMapper;
  @Autowired
  private ChartYearMapper chartYearMapper;
  @Autowired
  private OrderFinishedInfoMapper orderFinishedInfoMapper;

  private static final Logger logger = LoggerFactory.getLogger(DefaultChartService.class);

  private static final ReentrantLock lock = new ReentrantLock();

  /**
   * 增加统计记录.
   */
  @Transactional(rollbackFor = Exception.class)
  public void addStatisticsRecord(Order order, Long mainCompany) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    Date date = new Date();
    String queryTime = sdf.format(date);
    if (order.getStatus().intValue() == 5) {
      OrderFinishedInfo tempOfi = null;
      Integer finishtype = null;
      Integer runtype = null;
      if (order.getCompanyId().equals(order.getEmployCompanyId())
          && order.getCompanyId().equals(mainCompany)) { // 本公司订单并且由本公司服务人员完成单量
        tempOfi =
            orderFinishedInfoMapper.selectByCompanyIdAndAppKeyAndType(order.getCompanyId(),
                order.getAppkey(), OrderFinishedInfo.TYPE_ORDERFINISHEDAMOUNT_THIS, queryTime);
        finishtype = OrderFinishedInfo.TYPE_ORDERFINISHEDAMOUNT_THIS;
        runtype = OrderFinishedInfo.TYPE_ORDERBUSYAMOUNT_THIS;
      } else if (!order.getCompanyId().equals(order.getEmployCompanyId())
          && order.getCompanyId().equals(mainCompany)) {
        tempOfi =
            orderFinishedInfoMapper.selectByCompanyIdAndAppKeyAndType(order.getCompanyId(),
                order.getAppkey(), OrderFinishedInfo.TYPE_ORDERFINISHEDAMOUNT_GETOUT, queryTime);
        finishtype = OrderFinishedInfo.TYPE_ORDERFINISHEDAMOUNT_GETOUT;
        runtype = OrderFinishedInfo.TYPE_ORDERBUSYAMOUNT_GETOUT;
      } else if (!order.getCompanyId().equals(mainCompany)
          && order.getEmployCompanyId().equals(mainCompany)) {
        tempOfi =
            orderFinishedInfoMapper.selectByCompanyIdAndAppKeyAndType(order.getCompanyId(),
                order.getAppkey(), OrderFinishedInfo.TYPE_ORDERFINISHEDAMOUNT_COMEIN, queryTime);
        finishtype = OrderFinishedInfo.TYPE_ORDERFINISHEDAMOUNT_COMEIN;
        runtype = OrderFinishedInfo.TYPE_ORDERBUSYAMOUNT_COMEIN;
      }

      if (tempOfi == null) {
        OrderFinishedInfo ofi = new OrderFinishedInfo();
        ofi.setAppKey(order.getAppkey());
        ofi.setCompanyId(order.getCompanyId());
        ofi.setCompanyName(order.getCompanyName());
        ofi.setType(finishtype);
        ofi.setOrderNumber(new BigDecimal(1));
        ofi.setOderTotalMoney(new BigDecimal(order.getRealPay() == null ? 0 : order.getRealPay()));
        ofi.setCouponMoney(new BigDecimal(order.getCouponMoney() == null ? 0 : order
            .getCouponMoney()));
        ofi.setBalanceMoney(new BigDecimal(order.getRealPay()
            - (order.getCouponMoney() == null ? 0 : order.getCouponMoney())));
        ofi.setCompanyMoney(new BigDecimal(order.getCompanyProfit() == null ? 0 : order
            .getCompanyProfit()));
        ofi.setDriverMoney(new BigDecimal(order.getEmployProfit() == null ? 0 : order
            .getEmployProfit()));
        ofi.setGuoluMoney(
            new BigDecimal(order.getGuoluMoney() == null ? 0 : order.getGuoluMoney()));
        orderFinishedInfoMapper.insert(ofi);
      } else {
        tempOfi.setOrderNumber(tempOfi.getOrderNumber().add(new BigDecimal(1)));
        tempOfi.setGuoluMoney(tempOfi.getGuoluMoney().add(new BigDecimal(order.getGuoluMoney())));
        tempOfi.setOderTotalMoney(tempOfi.getOderTotalMoney().add(
            new BigDecimal(order.getRealPay() == null ? 0 : order.getRealPay())));
        tempOfi.setCouponMoney(tempOfi.getCouponMoney().add(
            new BigDecimal(order.getCouponMoney() == null ? 0 : order.getCouponMoney())));
        tempOfi.setBalanceMoney(tempOfi.getBalanceMoney().add(
            new BigDecimal(order.getRealPay()
                - (order.getCouponMoney() == null ? 0 : order.getCouponMoney()))));
        orderFinishedInfoMapper.update(tempOfi);
        OrderFinishedInfo ofi =
            orderFinishedInfoMapper.selectByCompanyIdAndAppKeyAndType(order.getCompanyId(),
                order.getAppkey(), runtype, queryTime);
        if (ofi != null) {
          ofi.setOrderNumber(ofi.getOrderNumber().subtract(new BigDecimal(1)));
          orderFinishedInfoMapper.update(ofi);
        }
      }


    } else if (order.getStatus().intValue() == 6) {
      Integer runtype = null;
      if (order.getCompanyId().equals(mainCompany)
          && order.getCompanyId().equals(order.getEmployCompanyId())) {
        runtype = OrderFinishedInfo.TYPE_ORDERBUSYAMOUNT_THIS;
      } else if (order.getCompanyId().equals(mainCompany)
          && !order.getCompanyId().equals(order.getEmployCompanyId())) {
        runtype = OrderFinishedInfo.TYPE_ORDERBUSYAMOUNT_GETOUT;
      } else if (!order.getCompanyId().equals(mainCompany)
          && mainCompany.equals(order.getEmployCompanyId())) {
        runtype = OrderFinishedInfo.TYPE_ORDERBUSYAMOUNT_COMEIN;
      }
      OrderFinishedInfo tempOfi =
          orderFinishedInfoMapper.selectByCompanyIdAndAppKeyAndType(order.getCompanyId(),
              order.getAppkey(), OrderFinishedInfo.TYPE_ORDERCANCELEDAMOUNT, queryTime);
      if (runtype != null && tempOfi != null) {
        tempOfi.setOrderNumber(tempOfi.getOrderNumber().add(new BigDecimal(1)));
        orderFinishedInfoMapper.update(tempOfi);
        OrderFinishedInfo ofi =
            orderFinishedInfoMapper.selectByCompanyIdAndAppKeyAndType(order.getCompanyId(),
                order.getAppkey(), runtype, queryTime);
        if (tempOfi != null) {
          ofi.setOrderNumber(ofi.getOrderNumber().subtract(new BigDecimal(1)));
          orderFinishedInfoMapper.update(ofi);
        }
      } else if (runtype != null && tempOfi == null) {
        OrderFinishedInfo cancelOfi = new OrderFinishedInfo();
        cancelOfi.setAppKey(order.getAppkey());
        cancelOfi.setCompanyId(order.getCompanyId());
        cancelOfi.setCompanyName(order.getCompanyName());
        cancelOfi.setType(OrderFinishedInfo.TYPE_ORDERCANCELEDAMOUNT);
        cancelOfi.setOrderNumber(new BigDecimal(1));
        orderFinishedInfoMapper.insert(cancelOfi);
      } else if (runtype == null && tempOfi == null) {
        OrderFinishedInfo cancelOfi = new OrderFinishedInfo();
        cancelOfi.setAppKey(order.getAppkey());
        cancelOfi.setCompanyId(order.getCompanyId());
        cancelOfi.setCompanyName(order.getCompanyName());
        cancelOfi.setType(OrderFinishedInfo.TYPE_ORDERCANCELEDAMOUNT);
        cancelOfi.setOrderNumber(new BigDecimal(1));
        orderFinishedInfoMapper.insert(cancelOfi);
      } else if (runtype == null && tempOfi != null) {
        tempOfi.setOrderNumber(tempOfi.getOrderNumber().add(new BigDecimal(1)));
        orderFinishedInfoMapper.update(tempOfi);
      }
    } else if (order.getStatus().intValue() > 0 && order.getStatus().intValue() < 5) {
      Integer runType = null;
      if (order.getCompanyId().equals(mainCompany)
          && mainCompany.equals(order.getEmployCompanyId())) {
        runType = OrderFinishedInfo.TYPE_ORDERBUSYAMOUNT_THIS;
      } else if (order.getCompanyId().equals(mainCompany)
          && !mainCompany.equals(order.getEmployCompanyId())) {
        runType = OrderFinishedInfo.TYPE_ORDERBUSYAMOUNT_GETOUT;
      } else if (!order.getCompanyId().equals(mainCompany)
          && mainCompany.equals(order.getEmployCompanyId())) {
        runType = OrderFinishedInfo.TYPE_ORDERBUSYAMOUNT_COMEIN;
      }
      OrderFinishedInfo tempOfi =
          orderFinishedInfoMapper.selectByCompanyIdAndAppKeyAndType(order.getCompanyId(),
              order.getAppkey(), runType, queryTime);
      if (tempOfi != null) {
        tempOfi.setOrderNumber(tempOfi.getOrderNumber().add(new BigDecimal(1)));
        orderFinishedInfoMapper.update(tempOfi);
      } else {
        OrderFinishedInfo ofi = new OrderFinishedInfo();
        ofi.setAppKey(order.getAppkey());
        ofi.setCompanyId(order.getCompanyId());
        ofi.setCompanyName(order.getCompanyName());
        ofi.setType(runType);
        ofi.setOrderNumber(new BigDecimal(1));
        orderFinishedInfoMapper.insert(ofi);
      }


    } else {
      logger.warn(
          "order: {}'s status is not finished or canceled or run, it can not be add to day record",
          order.getOrderNo());
    }
  }


  @Override
  public  void addDayRecord(Order order) {
    lock.lock();
    try {
      Calendar calendar = Calendar.getInstance();
      int year = calendar.get(Calendar.YEAR);
      int month = calendar.get(Calendar.MONTH);
      int day = calendar.get(Calendar.DAY_OF_MONTH);
      int hour = calendar.get(Calendar.HOUR_OF_DAY);
      if (order.getStatus().equals(5)) {
        // 增加年记录

        ChartYear chartYear =
            chartYearMapper.selectByYearAndCompany(year, order.getCompanyId(), order.getAppkey());

        if (null == chartYear) {
          chartYear = new ChartYear();
          chartYear.setYear(year);
          chartYear.setCompanyId(order.getCompanyId());
          chartYear.setCompanyName(order.getCompanyName());
          chartYear.setTotalSuccessNum(1L);
          chartYear.setTotalPrice(new BigDecimal(order.getRealPay()));
          chartYear.setAppKey(order.getAppkey());
          chartYearMapper.insertRecord(chartYear);
        } else {
          chartYear.setTotalSuccessNum(chartYear.getTotalSuccessNum() + 1L);
          chartYear.setTotalPrice(chartYear.getTotalPrice().add(new BigDecimal(order.getRealPay())));
          chartYearMapper.updateByPrimaryKey(chartYear);
        }

        // 增加月记录
        ChartMonth chartMonth =
            chartMonthMapper.selectByYearAndMonth(chartYear.getId(), month, order.getAppkey());
        if (null == chartMonth) {
          chartMonth = new ChartMonth();
          chartMonth.setMonth(month);
          chartMonth.setYearId(chartYear.getId());
          chartMonth.setTotalSuccessNum(1);
          chartMonth.setTotalPrice(new BigDecimal(order.getRealPay()));
          chartMonth.setAppKey(order.getAppkey());
          chartMonthMapper.insertRecord(chartMonth);
        } else {
          chartMonth.setTotalSuccessNum(chartMonth.getTotalSuccessNum() + 1L);
          chartMonth
              .setTotalPrice(chartMonth.getTotalPrice().add(new BigDecimal(order.getRealPay())));
          chartMonthMapper.updateByPrimaryKey(chartMonth);
        }

        // 增加日记录
        ChartDay chartDay =
            chartDayMapper.selectByMonthAndDay(chartMonth.getId(), day, order.getAppkey());

        if (null == chartDay) {
          chartDay = new ChartDay();
          chartDay.setMonthId(chartMonth.getId());
          chartDay.setDay(day);
          chartDay.setTotalSuccessNum(1);
          chartDay.setAppKey(order.getAppkey());
          chartDay.setTotalPrice(new BigDecimal(order.getRealPay()));
        } else {
          chartDay.setTotalSuccessNum(chartDay.getTotalSuccessNum() + 1L);
          chartDay.setTotalPrice(chartDay.getTotalPrice().add(new BigDecimal(order.getRealPay())));
        }

        try {
          Class<ChartDay> clazz = ChartDay.class;

          Method getMethod = ReflectionUtils.findMethod(clazz, "getHour" + hour + "SuccessNum");
          Method setMethod =
              ReflectionUtils.findMethod(clazz, "setHour" + hour + "SuccessNum", long.class);

          long num = (Long) ReflectionUtils.invokeMethod(getMethod, chartDay);
          ReflectionUtils.invokeMethod(setMethod, chartDay, num + 1L);

          getMethod = ReflectionUtils.findMethod(clazz, "getHour" + hour + "TotalPrice");
          setMethod =
              ReflectionUtils.findMethod(clazz, "setHour" + hour + "TotalPrice", BigDecimal.class);

          BigDecimal price = (BigDecimal) ReflectionUtils.invokeMethod(getMethod, chartDay);
          ReflectionUtils.invokeMethod(setMethod, chartDay,
              price.add(new BigDecimal(order.getRealPay())));

        } catch (SecurityException ex) {
          ex.printStackTrace();
        }

        if (null == chartDay.getId()) {
          chartDayMapper.insertRecord(chartDay);
        } else {
          chartDayMapper.updateByPrimaryKey(chartDay);
        }

      } else if (order.getStatus().equals(6)) {
        calendar = Calendar.getInstance();
        calendar.setTime(order.getCreated());
         year = calendar.get(Calendar.YEAR);
         month = calendar.get(Calendar.MONTH);
         day = calendar.get(Calendar.DAY_OF_MONTH);
         hour = calendar.get(Calendar.HOUR_OF_DAY);

        // 增加年记录
        ChartYear chartYear =
            chartYearMapper.selectByYearAndCompany(year, order.getCompanyId(), order.getAppkey());
        if (null == chartYear) {
          chartYear = new ChartYear();
          chartYear.setYear(year);
          chartYear.setCompanyId(order.getCompanyId());
          chartYear.setCompanyName(order.getCompanyName());
          chartYear.setTotalCancelNum(1);
          chartYear.setAppKey(order.getAppkey());
          chartYearMapper.insertRecord(chartYear);
        } else {
          chartYear.setTotalCancelNum(chartYear.getTotalCancelNum() + 1);
          chartYearMapper.updateByPrimaryKey(chartYear);
        }

        // 增加月记录
        ChartMonth chartMonth =
            chartMonthMapper.selectByYearAndMonth(chartYear.getId(), month, order.getAppkey());

        if (null == chartMonth) {
          chartMonth = new ChartMonth();
          chartMonth.setMonth(month);
          chartMonth.setYearId(chartYear.getId());
          chartMonth.setTotalCancelNum(1);
          chartMonth.setAppKey(order.getAppkey());
          chartMonthMapper.insertRecord(chartMonth);
        } else {
          chartMonth.setTotalCancelNum(chartMonth.getTotalCancelNum() + 1);
          int i = chartMonthMapper.updateByPrimaryKey(chartMonth);
        }

        // 增加日记录
        ChartDay chartDay =
            chartDayMapper.selectByMonthAndDay(chartMonth.getId(), day, order.getAppkey());

        if (null == chartDay) {
          chartDay = new ChartDay();
          chartDay.setMonthId(chartMonth.getId());
          chartDay.setDay(day);
          chartDay.setAppKey(order.getAppkey());
          chartDay.setTotalCancelNum(1);
        } else {
          chartDay.setTotalCancelNum(chartDay.getTotalCancelNum() + 1);
        }

        try {

          Class<ChartDay> clazz = ChartDay.class;

          Method getMethod = ReflectionUtils.findMethod(clazz, "getHour" + hour + "CancelNum");
          Method setMethod =
              ReflectionUtils.findMethod(clazz, "setHour" + hour + "CancelNum", long.class);

          long num = (Long) ReflectionUtils.invokeMethod(getMethod, chartDay);
          ReflectionUtils.invokeMethod(setMethod, chartDay, num + 1);

        } catch (SecurityException ex) {
          ex.printStackTrace();
        }

        if (null == chartDay.getId()) {
          chartDayMapper.insertRecord(chartDay);
        } else {
          chartDayMapper.updateByPrimaryKey(chartDay);
        }

      } else {
        logger.warn(
            "order: {}'s status is not finished or canceled, it can not be add to day record",
            order.getOrderNo());
      }
    } catch (Exception ex) {
      ex.printStackTrace();
      logger.error(
          "order: {}'s status is not finished or canceled, it can not be add to day record",
          ex);
    }finally {
      lock.unlock();
    }
  }

  @Override
  public ChartYear dataForYear(Integer year, Long companyId, String appKey) {
    ChartYear chartYear = chartYearMapper.selectByYearAndCompany(year, companyId, appKey);
    if (chartYear != null) {
      List<ChartMonth> chartMonths = chartMonthMapper.selectMonthByYear(chartYear.getId(), appKey);
      List<ChartMonth> temp = new LinkedList<>();
      for (int i = 0; i <= 11; i++) {
        boolean flag = true;
        for (int j = 0; j < chartMonths.size(); j++) {
          if (i == chartMonths.get(j).getMonth()) {
            flag = false;
            break;
          }
        }
        if (flag) {
          ChartMonth cm = new ChartMonth();
          cm.setMonth(i);
          cm.setTotalPrice(BigDecimal.ZERO);
          cm.setYearId(chartYear.getId());
          cm.setTotalCancelNum(0L);
          cm.setTotalSuccessNum(0L);
          temp.add(cm);
        }
      }
      chartMonths.addAll(temp);
      chartYear.setMonths(chartMonths);
    }

    return chartYear;
  }

  @Override
  public ChartMonth dataForMonth(Integer year, Integer month, Long companyId, String appKey) {
    ChartYear chartYear = chartYearMapper.selectByYearAndCompany(year, companyId, appKey);
    ChartMonth chartMonth = null;

    if (chartYear != null) {
      chartMonth = chartMonthMapper.selectByYearAndMonth(chartYear.getId(), month, appKey);
      if (chartMonth != null) {
        List<ChartDay> chartDays = chartDayMapper.selectDayByMonth(chartMonth.getId(), appKey);
        List<ChartDay> temp = new LinkedList<>();
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.MONTH, month);
        int maxDay = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
        for (int i = 1; i < maxDay; i++) {
          boolean flag = true;
          for (ChartDay cd : chartDays) {
            if (i == cd.getDay()) {
              flag = false;
              break;
            }
          }
          if (flag) {
            ChartDay cd = new ChartDay();
            cd.setTotalCancelNum(0L);
            cd.setTotalPrice(BigDecimal.ZERO);
            cd.setTotalSuccessNum(0L);
            cd.setDay(i);
            cd.setMonthId(chartMonth.getId());
            temp.add(cd);
          }
        }
        chartDays.addAll(temp);
        chartMonth.setChartDays(chartDays);
      }
    }
    return chartMonth;
  }

  @Override
  public ChartDay dataForChartDay(Integer year, Integer month, Integer day, Long companyId,
      String appKey) {
    ChartYear chartYear = chartYearMapper.selectByYearAndCompany(year, companyId, appKey);
    ChartDay chartDay = null;
    if (chartYear != null) {
      ChartMonth chartMonth =
          chartMonthMapper.selectByYearAndMonth(chartYear.getId(), month, appKey);
      if (chartMonth != null) {
        chartDay = chartDayMapper.selectByMonthAndDay(chartMonth.getId(), day, appKey);
        if (chartDay != null) {
          List<HourChart> hours = new LinkedList<HourChart>();
          Class<ChartDay> clazz = ChartDay.class;
          for (int hour = 0; hour < 24; hour++) {
            HourChart hc = new HourChart();
            if (null != chartDay) {
              Method getMethod = ReflectionUtils.findMethod(clazz, "getHour" + hour + "CancelNum");
              hc.setTotalCancel((Long) ReflectionUtils.invokeMethod(getMethod, chartDay));

              getMethod = ReflectionUtils.findMethod(clazz, "getHour" + hour + "SuccessNum");
              hc.setTotalSuccess((Long) ReflectionUtils.invokeMethod(getMethod, chartDay));

              getMethod = ReflectionUtils.findMethod(clazz, "getHour" + hour + "TotalPrice");
              hc.setTotalPrice((BigDecimal) ReflectionUtils.invokeMethod(getMethod, chartDay));
            }
            hc.setHour(hour);
            hours.add(hc);
          }
          chartDay.setHours(hours);
        }
      }
    }
    return chartDay;
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void updateRealMoney(Order order, Double oldRealPay) {
    Calendar cal = Calendar.getInstance();
    cal.setTime(order.getFinishTime());
    int year = cal.get(Calendar.YEAR);

    ChartYear chartYear =
        chartYearMapper.selectByYearAndCompany(year, order.getCompanyId(), order.getAppkey());
    if (chartYear != null) {
      BigDecimal bdValue = new BigDecimal(oldRealPay).subtract(new BigDecimal(order.getRealPay()));
      chartYear.setTotalPrice(chartYear.getTotalPrice().add(bdValue));
      chartYearMapper.updateByPrimaryKey(chartYear);
      ChartMonth chartMonth =
          chartMonthMapper.selectByYearAndMonth(chartYear.getId(), cal.get(Calendar.MONTH),
              order.getAppkey());
      if (chartMonth != null) {
        chartMonth.setTotalPrice(chartMonth.getTotalPrice().add(bdValue));
        chartMonthMapper.updateByPrimaryKey(chartMonth);
        ChartDay chartDay =
            chartDayMapper.selectByMonthAndDay(chartMonth.getId(), cal.get(Calendar.DAY_OF_MONTH),
                order.getAppkey());
        if (chartDay != null) {
          chartDay.setTotalPrice(chartDay.getTotalPrice().add(bdValue));
          int hour = cal.get(Calendar.HOUR_OF_DAY);
          Class<ChartDay> clazz = ChartDay.class;
          Method getMethod = ReflectionUtils.findMethod(clazz, "getHour" + hour + "TotalPrice");
          chartDay.setTotalPrice(((BigDecimal) ReflectionUtils.invokeMethod(getMethod, chartDay))
              .add(bdValue));
          chartDayMapper.updateByPrimaryKey(chartDay);

        }
      }
    }
  }
}
